<body>

<div id="app"></div>
</body>
<script>
    /**
     * 渲染摸快
     */
    function h(tag, props, children) {
        return {
            tag,
            props,
            children
        }
    }
    // 挂载函数
    function mount(vnode, container) {
        // 创建vnode.tag节点
        const el = vnode.el = document.createElement(vnode.tag);
        /**
         * 只考虑属性的添加
         */
        // props
        if (vnode.props) {
            for (const key in vnode.props) {
                const value = vnode.props[key];
                if(key.startsWith('on')){
                    // 向节点添加事件
                    el.addEventListener(key.slice(2).toLocaleLowerCase(), value)
                }else{
                    // 向节点添加属性
                    el.setAttribute(key, value);
                }
            }
        }
        // children
        if (vnode.children) {
            if (typeof vnode.children === 'string') {
                // 当子节点是string时，直接挂载
                el.textContent = vnode.children;
            } else {
                vnode.children.forEach(child => {
                    mount(child, el)
                });
            }
        }
        // 将节点插入容器中
        container.appendChild(el)
    }
    // 更新节点
    function patch(n1, n2) {
        if (n1.tag == n2.tag) {
            const el = n2.el = n1.el;
            const oldProps = n1.props || {};
            const newProps = n2.props || {};
            console.log(oldProps, newProps)
            // props
            for (const key in newProps) {
                const oldValue = oldProps[key];
                const newValue = newProps[key];
                if (newValue !== oldValue) {
                    el.setAttribute(key, newValue)
                }
            }

            // 旧的el中的props的属性没有了
            for (const key in oldProps) {
                if (!(key in newProps)) {
                    el.removeAttribute(key)
                }
            }
            // children
            const oldChildren = n1.children;
            const newChildren = n2.children;
            // 新节点是字符串
            if (typeof newChildren === 'string') {
                // 老节点是字符串
                if (typeof oldChildren === 'string') {
                    if (newChildren !== oldChildren) {
                        el.textContent = newChildren
                    }
                } else {
                    el.textContent = newChildren
                }
            } else {
                // 老节点是字符串
                if (typeof oldChildren === 'string') {
                    // 清空el节点下的元素
                    el.innerHTML = '';
                    // 添加新节点
                    newChildren.forEach(child => {
                        mount(child, el)
                    })
                } else {

                    console.log(oldChildren, newChildren)
                    // oldChildren、newChildren都是数组
                    const commonLength = Math.min(oldChildren.length, newChildren.length);
                    for (let i = 0; i < commonLength; i++) {
                        patch(oldChildren[i], newChildren[i])
                    }
                    if (newChildren.length > oldChildren.length) {
                        newChildren.slice(oldChildren.length).forEach(child => {
                            mount(child, el)
                        })
                    } else if (newChildren.length < oldChildren.length) {
                        oldChildren.slice(newChildren.length).forEach(child => {
                            el.removeAttribute(child.el)
                        })
                    }
                }
            }

        } else {
            // 替换
        }
    }
    /** 
     * 响应模块
     */
    // 当前的订阅者
    let activeEffect;
    // 依赖追踪类
    class Dep {
        // 订阅者集合
        subscribers = new Set();
        // 添加订阅者
        depend() {
            if (activeEffect) {
                this.subscribers.add(activeEffect);
            }
        }
        // 通知订阅者
        notify() {
            this.subscribers.forEach(effect => {
                effect()
            })
        }
    }
    // 保留此次订阅
    function watchEffect(effect) {
        activeEffect = effect;
        effect();
        activeEffect = null;
    }
    // 存储所有的值
    const targetMap = new WeakMap();
    // 获取到当前的dep对象
    function getDep(target, key) {
        // 判断在targetMap中是否有目标对象
        let depsMap = targetMap.get(target);
        if (!depsMap) {
            depsMap = new Map();
            targetMap.set(target, depsMap)
        }
        // 判断是否key在depsMap对象中
        let dep = depsMap.get(key)
        if (!dep) {
            dep = new Dep();
            // 添加dep对象至depsMap对象中
            depsMap.set(key, dep)
        }
        return dep;
    }
    // reactiveHandlers声明了代理target的指定行为
    const reactiveHandlers = {
        get(target, key, receiver) {
            const dep = getDep(target, key);
            dep.depend();
            return Reflect.get(target, key, receiver);
        },
        set(target, key, value, receiver) {
            const dep = getDep(target, key);
            const result = Reflect.set(target, key, value, receiver);
            dep.notify();
            return result;
        },
    }
    // 双向绑定代理
    function reactive(raw) {
        return new Proxy(raw, reactiveHandlers)
    }

    const App = {
        data:reactive({
            count:0
        }),
        render(){
            return h('div', {
                onclick:() =>{
                this.data.count++
            }
        }, String(this.data.count))
        }
    }

    function mountApp(component, container){
        watchEffect(() =>{
            let isMounted = false;
            // 旧节点   
            let preValue;
            watchEffect(() => {
                if(!isMounted){
                    preValue = component.render();
                    mount(preValue, container);
                    isMounted = true;
                }else{
                    const newValue = component.render();
                    // 对比节点不同,并一一挂载
                    patch(preValue, newValue)
                    preValue = newValue;    
                }
            })
        })
    }
    mountApp(App, document.getElementById('app'))
    /** 编译模块 */
</script>